# from functools import wraps, partial
#
# import logging
#
# logging.basicConfig(level=logging.DEBUG)
#
#
# # 可自定义属性的装饰器
#
# def attach_wrapper(obj, func=None):
#     if func is None:
#         return partial(attach_wrapper, obj)
#     setattr(obj, func.__name__, func)
#
#
# def logged(level, name=None, message=None):
#     def decorate(func):
#         logname = name if name else func.__module__
#         log = logging.getLogger(logname)
#         logmsg = message if message else func.__name__
#
#         @wraps(func)
#         def wrapper(*args, **kwargs):
#             log.log(level, logmsg)
#             return func(*args, **kwargs)
#
#         @attach_wrapper(wrapper)
#         def set_level(newlevel):
#             nonlocal level
#             level = newlevel
#
#         @attach_wrapper(wrapper)
#         def set_message(newmsg):
#             nonlocal logmsg
#             logmsg = newmsg
#
#         return wrapper
#
#     return decorate
#
#
# @logged(logging.DEBUG)
# def add(x, y):
#     return x + y
#
#
# @logged(logging.CRITICAL, 'example')
# def spam():
#     print('Spam!')


# 使用装饰器强制函数的类型检查
from inspect import signature
from functools import wraps

def typeassert(*ty_args, **ty_kwargs):
    def decorate(func):
        # if in optimized mode, disable type checking
        if not __debug__:
            return func
        # map funcion argument names to suplied types
        sig = signature(func)
        bound_types = sig.bind_partial(*ty_args, **ty_kwargs).arguments

        print(ty_args)
        print(bound_types)


        @wraps(func)
        def wrapper(*args, **kwargs):
            bound_values = sig.bind(*args, **kwargs)
            # enforce type assertions across suplied arguments
            for name, value in bound_values.arguments.items():
                # print(name, value)
                # print(args, 'args')
                if name in bound_types:
                    if not isinstance(value, bound_types[name]):
                        raise TypeError('Argument {} must be {}'.format(name, bound_types[name]))
                # else:
                #     if not isinstance(value, ty_args[0]):
                #         raise TypeError('Argument {} must be {}'.format(name, ty_args[0]))

            return func(*args, **kwargs)
        return wrapper
    return decorate


@typeassert(int, z=int)
def spam2(x,y, z=42):
    print(x,y,z)

spam2(1,2,3)
# spam2(1, 'hello', 4)
# spam2(1, 2, 4)